#include "map.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstring> // for memset
#include <cstdlib> // for atoi

//#define VERBOSE_OUTPUT

#ifdef VERBOSE_OUTPUT
#  define DEBUG(x) std::cout << x << std::endl;
#else
#  define DEBUG(x) // x
#endif // VERBOSE_OUTPUT

Map::Map(void)
{
  w = 0;
  h = 0;
  _size = 0;
  dataPtr = 0;
}

Map::~Map(void)
{
  clear();
}

void Map::clear()
{
  delete [] dataPtr;
  w = 0;
  h = 0;
  _size = 0;
  dataPtr = 0;
}

void Map::setData(int width, int height, const int* d)
{
  resize(width, height);
  memcpy(dataPtr, d, sizeof(int)*_size);
}

void Map::resize(int width, int height)
{
  clear();
  w = width;
  h = height;
  _size = w*h;
  
  dataPtr = new int[_size];
  memset(dataPtr, 0, sizeof(int)*_size);
}

bool Map::isValid(int row, int col) const
{
  return (row >= 0 && row < h &&
          col >= 0 && col < w);
}

bool Map::isBarrier(int row, int col) const
{
  if (!isValid(row, col))
    return true;
  
  return (getValue(row, col) == 0);
}

int Map::index(int row, int col) const
{
  return (row*w + col);
}

int Map::getValue(int index) const
{
  return dataPtr[index];
}

int Map::getValue(int row, int col) const
{
  return dataPtr[index(row, col)];
}

void Map::setValue(int row, int col, int value)
{
  dataPtr[index(row, col)] = value;
}

void Map::getRowCol(int index, int& row, int& col) const
{
  row = index/w;
  col = index - row*w;
}

Map::bracket Map::operator [] (int row)
{
  return bracket(dataPtr, row*w);
}

bool Map::readFile(const std::string& path)
{
  bool success = true;

  std::ifstream fp;
  fp.open(path.c_str(), std::ios::in | std::ios::binary);

  if (fp)
  {
    enum ParseState 
    {
      PROPERTIES,
      DATA
    } state;

    char buffer [128];
    std::string line;
    std::string substr;
    int width = 0;
    int height = 0;
    state = PROPERTIES;

    // PROPERTIES
    if (state == PROPERTIES)
    {
      while (!fp.eof() && state == PROPERTIES)
      {
        fp.getline(buffer, 128);
        line = buffer;
        
        // For some reason, some of the lines have a '\r' at the end.  Could
        // this be because the file was writtin on Windows??
        if (line.find("\r") >= 0)
          line = line.substr(0, line.find("\r"));

        DEBUG("Reading property: \"" << line << "\"");

        if (line.length() > 0)
        {
          if (getStrAfterIfMatch(line, "Width:", substr))
          {
            width = atoi(substr.c_str());
          }
          else if (getStrAfterIfMatch(line, "Height:", substr))
          {
            height = atoi(substr.c_str());
          }
        }
        else
        {
          state = DATA;
        }
      }
    } // properties

    // DATA
    if (width && height) 
    {
      DEBUG("Reading data...");
      resize(width, height);
  
      // Level data.
      for (int row = 0; row < height; ++row)
      {
        // Make sure we're not beyond the end of file
        if (fp.eof())
        {
          std::cout << "[ERROR] Premature end of file!\n";
          success = false;
          break;
        }

        // Get the next line
        fp.getline(buffer, 128);
        line = buffer;
        DEBUG("Line: " << line);
        
        // Parse the data.  Split the string to get each value, then convert 
        // them to ints and add them to the data array.
        std::vector<std::string> strVals;
        std::istringstream iss(line);
        do {iss >> substr; strVals.push_back(substr);} while(iss);
        for (int col = 0; col < strVals.size(); ++col)
          dataPtr[index(row, col)] = atoi(strVals[col].c_str());
      }
    } // if (width & hight)
    fp.close();

    success = true;
  }
  return success;
}

void Map::print(std::ostream& os)
{
  os << "Width: " << h << "\n";
  os << "Height: " << w << "\n";
  os << std::endl;
  
  for (int row = 0; row < h; ++row)
  {
    for (int col = 0; col < w; ++col)
      os << dataPtr[index(row, col)] << " ";
    os << "\n";
  }
}

bool Map::getStrAfterIfMatch(const std::string& str, 
                             const std::string& key, 
                             std::string& value)
{
  if (str.length() > key.length() && 
      str.substr(0, key.length()) == key)
  {
    value = str.substr(key.length());
    return true;
  }
  return false;
}
